package com.labot.webapp.listener;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.labot.Constants;
import com.labot.authentication.ldap.activedirectory.DomainController;
import com.labot.mail.CustomMailSender;
import com.labot.model.Config;
import com.labot.service.AuditManager;
import com.labot.service.ConfigManager;
import com.labot.service.Dbi18nManager;
import com.labot.service.GenericManager;
import com.labot.service.LookupManager;
import com.labot.types.ConstantsType;
import com.labot.util.StackTraceUtils;

/**
 * <p>StartupListener class used to initialize and database settings
 * and populate any application-wide drop-downs.
 * <p/>
 * <p>Keep in mind that this listener is executed outside of OpenSessionInViewFilter,
 * so if you're using Hibernate you'll have to explicitly initialize all loaded data at the
 * GenericDao or service level to avoid LazyInitializationException. Hibernate.initialize() works
 * well for doing this.
 *
 * @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a>
 */
@EnableScheduling
public class StartupListener implements ServletContextListener {
    private static final Log log = LogFactory.getLog(StartupListener.class);

    private static Map<ConstantsType, String> configs = new HashMap<ConstantsType, String>();
    
    private static Dbi18nManager i18nManager;
    
    private static AuditManager auditManager;
    
    public static AuditManager getAuditManager() {
		return auditManager;
	}

	public static Dbi18nManager getI18Manager() {
    	return i18nManager;
    }
    
    public static void setDbi18nManager(Dbi18nManager dbi18n) {
    	i18nManager = dbi18n;
    }
    
    public static String getConfigValue(ConstantsType constant) {
    	return configs.get(constant);
    }

	/**
     * Devuelve true o false a partir de una Constant de la DB, en caso de empezar con t o T es true, sino false
     * @param constant
     * @return
     */
    public static Boolean isEnabledConfig(ConstantsType constant) {
    	String value = getConfigValue(constant);
    	if (value != null) {
    		if (value.trim().toLowerCase().startsWith("t")){
    			return true;
    		}
    	}
    	return false;
    }
    
    /**
     * Devuelve la lista de Configuraciones en cache
     * @return
     */
    public static Map<ConstantsType, String> getConfigs() {
    	return configs;
    }
    
    /**
     * Startup 
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public void contextInitialized(ServletContextEvent event) {
        log.debug("Initializing context...");

        ServletContext context = event.getServletContext();

        // Orion starts Servlets before Listeners, so check if the config
        // object already exists
        Map<String, Object> config = (HashMap<String, Object>) context.getAttribute(Constants.CONFIG);

        if (config == null) {
            config = new HashMap<String, Object>();
        }

        ApplicationContext ctx =
                WebApplicationContextUtils.getRequiredWebApplicationContext(context);

        ShaPasswordEncoder passwordEncoder = null;
        try {
            ProviderManager provider = (ProviderManager) ctx.getBean("org.springframework.security.authentication.ProviderManager#0");
            for (Object o : provider.getProviders()) {
                AuthenticationProvider p = (AuthenticationProvider) o;
                if (p instanceof RememberMeAuthenticationProvider) {
                    config.put("rememberMeEnabled", Boolean.TRUE);
                } else if (ctx.getBean("passwordEncoder") != null) {
                    passwordEncoder = (ShaPasswordEncoder) ctx.getBean("passwordEncoder");
                }
            }
        } catch (NoSuchBeanDefinitionException n) {
            log.debug("authenticationManager bean not found, assuming test and ignoring...");
            // ignore, should only happen when testing
        }

        context.setAttribute(Constants.CONFIG, config);

        // output the retrieved values for the Init and Context Parameters
        if (log.isDebugEnabled()) {
            log.debug("Remember Me Enabled? " + config.get("rememberMeEnabled"));
            if (passwordEncoder != null) {
                log.debug("Password Encoder: " + passwordEncoder.getClass().getSimpleName());
            }
            log.debug("Populating drop-downs...");
        }

        setupContext(context);
    }
    
    private static void initializeConfig(ServletContext context) {
    	ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
    	ConfigManager mgr = (ConfigManager) ctx.getBean("configManager");
    	for (ConstantsType key : ConstantsType.values()) {
    		try {
    			if (mgr.getConfig(key) == null) {				
    				mgr.createNewConfig(key);
    			}    			
    		} catch (Exception e) {
    			log.error("ERROR when create a new Constant Config key.\n" + StackTraceUtils.getStackTrace(e));
    		}
		}
    	try {
    		mgr.deprecateConstants();
    	} catch (Exception e) {
    		e.printStackTrace();
    	}
    }
    
    public static void loadConfig(ServletContext context) {
    	ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
    	ConfigManager mgr = (ConfigManager) ctx.getBean("configManager");
    	CustomMailSender mail = (CustomMailSender) ctx.getBean("mailSender");
    	DomainController domainController = (DomainController) ctx.getBean("domainController");
    	i18nManager = (Dbi18nManager) ctx.getBean("i18nManager");
    	auditManager = (AuditManager) ctx.getBean("auditManager"); 
    	
    	List<Config> configList = mgr.getAllConfigs();
    	for (Config config : configList) {
			configs.put(config.getConstant(), config.getValue());
		}
    	
    	mail.setConfiguration();
    	log.debug("Sets of Mail properties              [OK]");
    	if (isEnabledConfig(ConstantsType.LDAP_ENABLED)) {    		
    		domainController.setConfiguration();
    		log.debug("Initialization of Domain Controler   [OK]");
    	} else {
    		log.debug("Initialization of Domain Controler   [DISABLED]");
    	}
    	
    }

    /**
     * This method uses the LookupManager to lookup available roles from the data layer.
     *
     * @param context The servlet context
     */
    public static void setupContext(ServletContext context) {
        ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
        LookupManager mgr = (LookupManager) ctx.getBean("lookupManager");
        
        // get list of possible roles
        context.setAttribute(Constants.AVAILABLE_ROLES, mgr.getAllRoles());
        log.debug("Drop-down initialization complete [OK]");

        // Any manager extending GenericManager will do:
        GenericManager<?, ?> manager = (GenericManager<?, ?>) ctx.getBean("userManager");
        doReindexing(manager);
        log.debug("Full text search reindexing complete [OK]");
        initializeConfig(context);
        log.debug("Initialize Config Constants complete [OK]");
        loadConfig(context);
        log.debug("Loading Config Constants from db     [OK]");
    }

    private static void doReindexing(GenericManager<?, ?> manager) {
        manager.reindexAll(false);
    }

    /**
     * Shutdown servlet context (currently a no-op method).
     *
     * @param servletContextEvent The servlet context event
     */
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        //LogFactory.release(Thread.currentThread().getContextClassLoader());
        //Commented out the above call to avoid warning when SLF4J in classpath.
        //WARN: The method class org.apache.commons.logging.impl.SLF4JLogFactory#release() was invoked.
        //WARN: Please see http://www.slf4j.org/codes.html for an explanation.
    }
}
